package com.jdd.fintech.morse.plugin;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jdd.fintech.morse.plugin.constans.OpenApiConstans;
import com.jdd.fintech.morse.plugin.ecrypts.SymmetricCrypto;
import com.jdd.fintech.morse.plugin.enums.*;
import com.jdd.fintech.morse.plugin.plugconfig.MorsePlugValueConfig;
import com.jdd.fintech.morse.plugin.strategy.context.DataEncryptAndDecryptStrategyContext;
import com.jdd.fintech.morse.plugin.util.CryptoUtils;
import com.jddglobal.filter.Filter;
import com.jddglobal.filter.data.Context;
import com.jddglobal.filter.data.Resource;
import com.jddglobal.filter.support.exception.FilterException;
import com.jddglobal.phevos.gateway.resources.ResourceCapability;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.util.List;

import static com.jdd.fintech.morse.plugin.constans.OpenApiConstans.DATA_IS_NULL;
import static com.jdd.fintech.morse.plugin.constans.OpenApiConstans.RESPONST_IS_NULL;
import static com.jdd.fintech.morse.plugin.util.CommonMethodUtils.getCustomerVersion;
import static com.jdd.fintech.morse.plugin.util.CommonMethodUtils.isItSymmetricalEncryptionAndDecryption;
import static com.jdd.fintech.morse.plugin.util.CryptoUtils.checkSecurityLength;


/**
 * @Description Morse 解密插件
 * @Author fengcheng
 * @Date 2021/02/05
 * @Version 1.0
 */
public class MoresOpenApiDecryptsFilter implements Filter {

    /**
     * 日志输出对象
     */
    private static final Logger log = LoggerFactory.getLogger(MoresOpenApiDecryptsFilter.class);

    @Override
    public <R, S> Resource<Object> filter(Context<R, S> context, List<ConfigurationValue> dynamic) throws FilterException {
        try {
            log.info("MoresOpenApiDecryptsFilter.filter.开始执行Morse-OpenApi-解密插件");
            //1.获取插件值中的配置
            if (CollectionUtils.isEmpty(dynamic)) {
                throw new FilterException(new ResourceCapability.AutoDefineResource(BizErrorCodeEnum.FAILURE_2.getCode(), BizErrorCodeEnum.FAILURE_2.getDesc()));
            }
            JSONObject jsonObject = null;
            MorsePlugValueConfig plugValueConfig = null;
            RequestDateDirectionEnum requestDateDirectionEnum = null;
            for (ConfigurationValue obj : dynamic) {
                String settingValue = obj.getSettingValue();
                if(StringUtils.isBlank(settingValue)){
                    continue;
                }
                JSONObject json = JSONObject.parseObject(settingValue);
                //获取插件值配置对象
                plugValueConfig = JSON.parseObject(json.toJSONString(), MorsePlugValueConfig.class);
                //获取参数入口方向
                requestDateDirectionEnum = plugValueConfig.getRequestDateDirection();
                if(RequestDateDirectionEnum.REQUEST.getCode().equals(requestDateDirectionEnum.getCode())){
                    jsonObject = JSON.parseObject(context.getRequest().getBody().toString(), JSONObject.class);
                    if (ObjectUtils.isEmpty(jsonObject)) {
                        log.error(DATA_IS_NULL);
                        return Resource.OK();
                    }
                } else{
                    //获取需要加密的明文字符串
                    Resource<Object> responseBody = context.getResponse().getBody();
                    if (ObjectUtils.isEmpty(responseBody)) {
                        log.error(OpenApiConstans.RESPONST_IS_NULL);
                        return Resource.OK();
                    }
                    JSONObject responseBodyJsonObject = JSON.parseObject(JSON.toJSONString(responseBody), JSONObject.class);
                    jsonObject = responseBodyJsonObject.getJSONObject("data");
                    if (ObjectUtils.isEmpty(jsonObject)) {
                        log.error(RESPONST_IS_NULL);
                        return Resource.OK();
                    }
                }
                log.debug("MoresOpenApiDecryptsFilter.filter.Morse-OpenApi-解密插件|解密插件-解密请求参数:{}", jsonObject.toJSONString());
                //是否是对称加解密，对称加密返回：true；非对称加解密返回：false
                boolean flag = isItSymmetricalEncryptionAndDecryption(plugValueConfig);
                if(flag){
                    //对称加解密，走此加解密逻辑，并开始构建加密对象
                    bulidAndEexecutDecrypts(plugValueConfig, jsonObject);
                } else {
                    //非对称加解密，走此加解密逻辑，并且不需要初始化加解密算法对象，默认为: null即可
                    decryptByVersion(plugValueConfig, jsonObject, null);
                }
            }
            String body;
            String address = context.getApi().getAddress();
            if(!ObjectUtils.isEmpty(plugValueConfig)){
                body = analysisDddressAndBuildRequest(plugValueConfig, address, jsonObject);
            } else {
                body = jsonObject.toJSONString();
            }
            log.debug("MoresOpenApiDecryptsFilter.filter.Morse-OpenApi-解密插件|执行完成, 解密成功后的数据：{}", body);
            if(RequestDateDirectionEnum.REQUEST.getCode().equals(requestDateDirectionEnum.getCode())){
                context.getRequest().setBody(body);
            } else{
                context.getResponse().setBody(Resource.<Object>OK(body));
            }
        } catch (Exception e) {
            log.error("Morse请求对象解密失败| 异常信息：{}", e.getMessage(), e);
            throw new FilterException(new ResourceCapability.AutoDefineResource(BizErrorCodeEnum.FAILURE_4.getCode(), BizErrorCodeEnum.FAILURE_4.getDesc()));
        }
        return Resource.OK();
    }

    /**
     * 开始构建解密对象并执行加密逻辑
     * @param plugValueConfig
     * @param jsonObject
     * @throws FilterException
     */
    public void bulidAndEexecutDecrypts(MorsePlugValueConfig plugValueConfig, JSONObject jsonObject) throws FilterException {
        if (BooleanEnum.YES.getCode().equals(plugValueConfig.getEncyAndDecyFlag().getCode())) {
            log.info("MoresOpenApiDecryptsFilter.bulidAndEexecutDecrypts.开始构建解密对象并执行加密逻辑");
            //获取插件值对象中密钥
            String privateSecurity = plugValueConfig.getPrivateSecurity();
            //检查加密相对应的密钥长度是否超长
            checkSecurityLength(privateSecurity, plugValueConfig);
            EncrAndDecrAlgorithmTypeEnum encyAndDecyType = plugValueConfig.getEncyAndDecyType();
            //加密模式
            String encryptMode = plugValueConfig.getEncryptMode().getValue();
            //密钥长度
            int secretKeyLength = plugValueConfig.getKeySize();
            //根据解密类型生成对称解密算法对象
            SymmetricCrypto scObj = CryptoUtils.generateSymmetricCrypto(encyAndDecyType, privateSecurity, encryptMode, secretKeyLength);
            //开始进行解密操作
            decryptByVersion(plugValueConfig, jsonObject, scObj);
        }  else {
            log.info("MoresOpenApiDecryptsFilter.bulidAndEexecutDecrypts.不需要进行解密，是否配置加解密配置值.encyAndDecyFlag: {}", plugValueConfig.getEncyAndDecyFlag().getCode());
        }
    }

    /**
     * 定制化解密
     * @param plugValueConfig
     * @param jsonObject
     * @param scObj
     * @return
     * @throws FilterException
     */
    public void decryptByVersion(MorsePlugValueConfig plugValueConfig, JSONObject jsonObject, SymmetricCrypto scObj) throws FilterException {
        log.info("MoresOpenApiDecryptsFilter.decryptByVersion.开始定制化解密逻辑");
        CustomVersionEnum customVersion = plugValueConfig.getCustomVersion();
        customVersion = getCustomerVersion(customVersion);
        //根据定制化版本获取对应的策略类
        DataEncryptAndDecryptStrategyContext context = new DataEncryptAndDecryptStrategyContext(customVersion);
        //调用数据解密策略方法
        context.dataDecryptsStrategyMethod(plugValueConfig, jsonObject, scObj);
    }

    /**
     * @方法描述: 解析地址并且构建请求参数
     * @方法名称: MoresOpenApiDecryptsFilter.analysisDddressAndBuildRequest
     * @方法操作人: ext.fengcheng
     * @方法生成时间: 2022/10/21 16:42
     * @方法执行参数: [plugValueConfig, address, jsonObject]
     * @方法返回参数: java.lang.String
     * @方法产生异常:
     **/
    public String analysisDddressAndBuildRequest(MorsePlugValueConfig plugValueConfig, String address, JSONObject jsonObject) throws FilterException {
        log.info("MoresOpenApiDecryptsFilter.analysisDddressAndBuildRequest.开始解析地址并且构建请求参数");
        String addressStr = address.substring(address.indexOf(OpenApiConstans.SLASH, 2) + 1, address.lastIndexOf(OpenApiConstans.SLASH));
        String[] split = addressStr.split(OpenApiConstans.SLASH);
        String tenantId = split[0];
        String productCode = split[1];
        //根据定制化版本获取对应的策略类
        DataEncryptAndDecryptStrategyContext context = new DataEncryptAndDecryptStrategyContext(plugValueConfig.getCustomVersion());
        //调用数据加密策略方法
        return context.buildCommonRequestObjMethod(plugValueConfig, tenantId, productCode, jsonObject);
    }
}
